%% Estado inicial do tabuleiro
estado_inicial([[0,0,0,0,0,0,1,0,0,0,0,0,0],
                [0,0,0,0,0,1,0,1,0,0,0,0,0],
                [0,0,0,0,1,0,1,0,1,0,0,0,0],
                [0,0,0,1,0,1,0,1,0,1,0,0,0],
                [0,0,1,0,1,0,1,0,1,0,1,0,0],
                [0,1,0,1,0,1,0,1,0,1,0,1,0],
                [1,0,1,0,1,0,1,0,1,0,1,0,1]]).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Alguns estados do tabuleiro para fazer alguns testes %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
estado_intermedio([[0,0,0,0,0,0,1,0,0,0,0,0,0],
                   [0,0,0,0,0,5,0,1,0,0,0,0,0],
                   [0,0,0,0,1,0,1,0,1,0,0,0,0],
                   [0,0,0,1,0,1,0,5,0,6,0,0,0],
                   [0,0,1,0,5,0,1,0,1,0,1,0,0],
                   [0,1,0,1,0,1,0,6,0,1,0,1,0],
                   [1,0,1,0,6,0,1,0,1,0,1,0,1]]).

%% Um estado aleatorio, chegado usando o predicado luta_entre_computadores
estado_intermedio2([[0,0,0,0,0,0,6,0,0,0,0,0,0],
                    [0,0,0,0,0,5,0,5,0,0,0,0,0],
                    [0,0,0,0,5,0,5,0,6,0,0,0,0],
                    [0,0,0,6,0,1,0,6,0,6,0,0,0],
                    [0,0,5,0,5,0,5,0,1,0,6,0,0],
                    [0,6,0,5,0,5,0,5,0,5,0,1,0],
                    [1,0,6,0,6,0,6,0,5,0,1,0,6]]).

%% Estados para testar os predicados do fim do jogo
estado_final([[0,0,0,0,0,0,6,0,0,0,0,0,0],
              [0,0,0,0,0,5,0,1,0,0,0,0,0],
              [0,0,0,0,5,0,1,0,5,0,0,0,0],
              [0,0,0,1,0,5,0,5,0,6,0,0,0],
              [0,0,6,0,5,0,1,0,6,0,1,0,0],
              [0,1,0,5,0,1,0,6,0,1,0,1,0],
              [1,0,5,0,6,0,1,0,1,0,1,0,6]]).

estado_final2([[0,0,0,0,0,0,6,0,0,0,0,0,0],
               [0,0,0,0,0,1,0,1,0,0,0,0,0],
               [0,0,0,0,5,0,1,0,5,0,0,0,0],
               [0,0,0,1,0,5,0,1,0,6,0,0,0],
               [0,0,6,0,1,0,5,0,6,0,5,0,0],
               [0,1,0,5,0,1,0,5,0,5,0,1,0],
               [1,0,5,0,6,0,1,0,5,0,1,0,6]]).


estado_final4([[0,0,0,0,0,0,5,0,0,0,0,0,0],
               [0,0,0,0,0,5,0,1,0,0,0,0,0],
               [0,0,0,0,5,0,5,0,6,0,0,0,0],
               [0,0,0,6,0,1,0,1,0,5,0,0,0],
               [0,0,6,0,1,0,1,0,1,0,6,0,0],
               [0,1,0,1,0,1,0,1,0,5,0,1,0],
               [1,0,6,0,1,0,6,0,5,0,6,0,6]]).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Predicados relacionados com a interface do utilizador %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Representacao, em caracteres, dos numeros que aparecem na
%% representacao interna do tabuleiro
escrever_caracter(0) :- write(' ').
escrever_caracter(1) :- write('.').
escrever_caracter(5) :- write('B').
escrever_caracter(6) :- write('P').

%% Escreve uma linha do tabuleiro no ecra, colocando um \n no fim
%% mostrar_linha(+Lista)
mostrar_linha([]) :- write('\n').
mostrar_linha([X|XS]) :- escrever_caracter(X), mostrar_linha(XS).

%% Escreve um tabuleiro no ecra
%% mostrar_tabuleiro(+NumeroLinha, +Tabuleiro)
mostrar_tabuleiro(_, []) :- % Quando o tabuleiro chega ao fim, adicionamos as letras
        writeln('    B D F H J L'),
        writeln('   A C E G I K M').

mostrar_tabuleiro(N, [X|XS]) :-
        write(N), write('- '), mostrar_linha(X),
        N1 is N+1,
        mostrar_tabuleiro(N1, XS).

%% Predicado auxiliar para ser mais simples de chamar o
%% predicado mostrar_tabuleiro
%% mostrar_tabuleiro(+Tabuleiro)
mostrar_tabuleiro(Tabuleiro) :- mostrar_tabuleiro(1, Tabuleiro).

%% Indica na interface qual e o jogador que esta a jogar
indica_jogador(0) :- writeln('Vez do jogador B').
indica_jogador(1) :- writeln('Vez do jogador P').

%% Mostra no ecra a interface para o utilizador
%% mostrar_interface(+Tabuleiro, +Jogador)
mostrar_interface(Tabuleiro, Jogador) :-
        mostrar_tabuleiro(Tabuleiro),
        write('\n'),
        indica_jogador(Jogador),
        write('\n'),
        writeln('Onde deseja colocar a peca:'),
        write('[1-7, A-M] ').

%% Predicado auxiliar para testes
mostrar_interface(Tabuleiro) :- mostrar_interface(Tabuleiro, 0).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%% Fim dos predicados da interface do utilizador %%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


%% Devolve true se se poder colocar uma peca nessa casa
%% (casa =:= 1).
%% jogada_valida(+Numero, +Letra, +Tabuleiro)
jogada_valida(Numero, Letra, Tabuleiro) :-
        integer(Numero), % Confirmar que Numero e de facto um numero
        Numero1 is Numero - 1, % Mudar de representacao do utilizador
                               % para a representacao interna
        obter_linha(Numero1, Tabuleiro, Linha),
        converter_letra_numero(Letra, LetraConv),
        obter_casa(LetraConv, Linha, X),
        X =:= 1.

%% Devolve true se o jogo tiver agora terminado
%% jogo_terminado(+Tabuleiro)
%% Para as pecas brancas
jogo_terminado(Tabuleiro) :-
        limite_esquerda(X, Y),
        %% So podemos chamar o predicado caminho se soubermos que
        %% nestas coordenadas esta uma peca branca
        obter_casa(X, Y, Tabuleiro, 5),
        caminho(X, Y, Tabuleiro, 5),
        write('As pecas brancas ganharam').

%% Para as pecas pretas
jogo_terminado(Tabuleiro) :-
        limite_esquerda(X, Y),
        %% So podemos chamar o predicado caminho se soubermos que
        %% nestas coordenadas esta uma peca preta
        obter_casa(X, Y, Tabuleiro, 6),
        caminho(X, Y, Tabuleiro, 6),
        write('As pecas pretas ganharam').

%% Colocar uma pedra numa dada casa, com um dado numero e uma dada
%% letra
%% colocar_pedra(+Numero, +LetraConv, +Tabuleiro, +Peca, -NovoTabuleiro).
colocar_pedra(Numero, LetraConv, [X|XS], Peca, [X|ZS]) :-
        Numero > 0,
        Numero1 is Numero - 1,
        colocar_pedra(Numero1, LetraConv, XS, Peca, ZS).

colocar_pedra(0, LetraConv, [X|XS], Peca, [NovaLista|XS]) :-
        colocar_pedra_linha(LetraConv, X, Peca, NovaLista).

%% Colocar uma pedra numa posicao de uma lista
%% colocar_pedra_linha(+N, +Lista, +Peca, -NovaLista)
colocar_pedra_linha(0, [_|XS], Peca, [Peca|XS]).
colocar_pedra_linha(N, [X|XS], Peca, [X|ZS]) :-
        N1 is N - 1,
        colocar_pedra_linha(N1, XS, Peca, ZS).

%% Predicados usados para colocar pedras de uma dada cor numa casa
%% Para colocar uma pedra preta
%% colocar_pedra_preta(+Numero, +Letra, +Tabuleiro, -NovoTabuleiro)
colocar_pedra_preta(Numero, Letra, Tabuleiro, NovoTabuleiro) :-
        jogada_valida(Numero, Letra, Tabuleiro),
        converter_letra_numero(Letra, LetraConv),
        Numero1 is Numero - 1,
        colocar_pedra(Numero1, LetraConv, Tabuleiro, 6, NovoTabuleiro).

%% Para colocar uma pedra branca
%% colocar_pedra_branca(+Numero, +Letra, +Tabuleiro, -NovoTabuleiro)
colocar_pedra_branca(Numero, Letra, Tabuleiro, NovoTabuleiro) :-
        jogada_valida(Numero, Letra, Tabuleiro),
        converter_letra_numero(Letra, LetraConv),
        Numero1 is Numero - 1,
        colocar_pedra(Numero1, LetraConv, Tabuleiro, 5, NovoTabuleiro).

% Predicados auxiliares que indicam os numeros e letras que o utilizador
% pode introduzir
numero_valido(N) :- N >= 1, N =< 7.

letra_valida(a).
letra_valida(b).
letra_valida(c).
letra_valida(d).
letra_valida(e).
letra_valida(f).
letra_valida(g).
letra_valida(h).
letra_valida(i).
letra_valida(j).
letra_valida(k).
letra_valida(l).
letra_valida(m).

%% Numero da posicao de uma linha que corresponde a uma dada letra
converter_letra_numero(a, 0).
converter_letra_numero(b, 1).
converter_letra_numero(c, 2).
converter_letra_numero(d, 3).
converter_letra_numero(e, 4).
converter_letra_numero(f, 5).
converter_letra_numero(g, 6).
converter_letra_numero(h, 7).
converter_letra_numero(i, 8).
converter_letra_numero(j, 9).
converter_letra_numero(k, 10).
converter_letra_numero(l, 11).
converter_letra_numero(m, 12).

%% Obtem a N-linha de um tabuleiro
%% obter_linha(+Numero, +Tabuleiro, -Linha)
obter_linha(0, [X|_], X) :- !.
obter_linha(Numero, [_|XS], Lista) :-
        Numero =< 7,
        Numero1 is Numero - 1,
        obter_linha(Numero1, XS, Lista).

%% Obtem o valor da casa que esta na posicao N de uma linha do
%% tabuleiro
%% obter_casa(+Numero, +Lista, -ValorCasa)
obter_casa(0, [X|_], X) :- !.
obter_casa(Numero, [_|XS], Casa) :-
        Numero =< 12,
        Numero1 is Numero - 1,
        obter_casa(Numero1, XS, Casa).


%% Comeca um jogo novo
%% Predicado so para testes
jogo :- estado_inicial(Tabuleiro), mostrar_interface(Tabuleiro).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Codigo para detectar se um jogo ja terminou %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Provavelmente seria possivel calcular as ligacoes de forma
%% matematica
ligacao(0,6, 1,5).
ligacao(0,6, 1,7).

ligacao(1,5, 2,4).
ligacao(1,5, 2,6).
ligacao(1,7, 2,6).
ligacao(1,7, 2,8).
ligacao(1,5, 1,7). % No mesmo nivel

ligacao(2,4, 3,3).
ligacao(2,4, 3,5).
ligacao(2,6, 3,5).
ligacao(2,6, 3,7).
ligacao(2,8, 3,7).
ligacao(2,8, 3,9).
ligacao(2,4, 2,6). % No mesmo nivel
ligacao(2,6, 2,8). % No mesmo nivel

ligacao(3,3, 4,2).
ligacao(3,3, 4,4).
ligacao(3,5, 4,4).
ligacao(3,5, 4,6).
ligacao(3,7, 4,6).
ligacao(3,7, 4,8).
ligacao(3,9, 4,8).
ligacao(3,9, 4,10).
ligacao(3,3, 3,5). % No mesmo nivel
ligacao(3,5, 3,7). % No mesmo nivel
ligacao(3,7, 3,9). % No mesmo nivel

ligacao(4,2, 5,1).
ligacao(4,2, 5,3).
ligacao(4,4, 5,3).
ligacao(4,4, 5,5).
ligacao(4,6, 5,5).
ligacao(4,6, 5,7).
ligacao(4,8, 5,7).
ligacao(4,8, 5,9).
ligacao(4,10, 5,9).
ligacao(4,10, 5,11).
ligacao(4,2, 4,4).  % No mesmo nivel
ligacao(4,4, 4,6).  % No mesmo nivel
ligacao(4,6, 4,8).  % No mesmo nivel
ligacao(4,8, 4,10). % No mesmo nivel

ligacao(5,1, 6,0).
ligacao(5,1, 6,2).
ligacao(5,3, 6,2).
ligacao(5,3, 6,4).
ligacao(5,5, 6,4).
ligacao(5,5, 6,6).
ligacao(5,7, 6,6).
ligacao(5,7, 6,8).
ligacao(5,9, 6,8).
ligacao(5,9, 6,10).
ligacao(5,11, 6,10).
ligacao(5,11, 6,12).
ligacao(5,1, 5,3).  % No mesmo nivel
ligacao(5,3, 5,5).  % No mesmo nivel
ligacao(5,5, 5,7).  % No mesmo nivel
ligacao(5,7, 5,9).  % No mesmo nivel
ligacao(5,9, 5,11). % No mesmo nivel

ligacao(6,0, 6,2).   % No mesmo nivel
ligacao(6,2, 6,4).   % No mesmo nivel
ligacao(6,4, 6,6).   % No mesmo nivel
ligacao(6,6, 6,8).   % No mesmo nivel
ligacao(6,8, 6,10).  % No mesmo nivel
ligacao(6,10, 6,12). % No mesmo nivel

existe_ligacao(X1,Y1, X2,Y2) :- ligacao(X1,Y1, X2,Y2).
existe_ligacao(X1,Y1, X2,Y2) :- ligacao(X2,Y2, X1,Y1).

%% Pontos que limitam o tabuleiro
limite_esquerda(0,6).
limite_esquerda(1,5).
limite_esquerda(2,4).
limite_esquerda(3,3).
limite_esquerda(4,2).
limite_esquerda(5,1).
limite_esquerda(6,0).

limite_direita(0,6).
limite_direita(1,7).
limite_direita(2,8).
limite_direita(3,9).
limite_direita(4,10).
limite_direita(5,11).
limite_direita(6,12).

limite_fundo(6,0).
limite_fundo(6,2).
limite_fundo(6,4).
limite_fundo(6,6).
limite_fundo(6,8).
limite_fundo(6,10).
limite_fundo(6,12).

%% Uns predicados especiais para indicar se o ponto (X1,Y1)
%% corresponde a um vertice do tabuleiro
%% e_vertice_topo(+X1, +Y1)
e_vertice_topo(X1, Y1) :-
        limite_esquerda(X1, Y1),
        limite_direita(X1, Y1).

%% e_vertice_inferior_esquerdo(+X1, +Y1)
e_vertice_inferior_esquerdo(X1, Y1) :-
        limite_esquerda(X1, Y1),
        limite_fundo(X1, Y1).

%% e_vertice_inferior_direito(+X1, +Y1)
e_vertice_inferior_direito(X1, Y1) :-
        limite_direita(X1, Y1),
        limite_fundo(X1, Y1).


%% Predicado que indica que existe um caminho que comece na casa
%% (X1,Y1) e que toque nos 3 lados do tabuleiro Tabuleiro, passando so
%% por um certo tipo de pedras
%% caminho(+X1, +Y1, +Tabuleiro, +ValorPedra)
caminho(X1, Y1, Tabuleiro, ValorPedra) :-
        caminho(X1, Y1, Tabuleiro, ValorPedra, 0, 0, 0, [X1|[Y1|[]]]).

%% Predicado que coloca em X2, Y2 as coordenadas da proxima pedra do
%% caminho.
%% Essa pedra e caracterizada por tres factores:
%%   1. Tem que haver uma ligacao entre elas no tabuleiro
%%   2. A pedra tem que ser do mesmo tipo que a pedra nas
%%      coordenadas X1, Y1 (valor passado no argumento ValorPedra)
%%   3. O ponto X2, Y2 so pode ter sido visitado 0 ou 1 vezes.
%% Prototipo da funcao
%% proxima_pedra_caminho(+X1, +Y1, +Tabuleiro, +ValorPedra, +PontosVisitados, -X2, -Y2)
proxima_pedra_caminho(X1, Y1, Tabuleiro, ValorPedra, PontosVisitados, X2, Y2) :-
        existe_ligacao(X1, Y1, X2, Y2),
        obter_casa(X2, Y2, Tabuleiro, ValorPedra),
        ponto_em_lista(X2, Y2, PontosVisitados).

%% O prototipo dos predicados caminho a seguir sao:
%% caminho(+X1, +Y1, +Tabuleiro, +ValorPedra,
%%         +LimiteEsquerda, +LimiteDireita, +LimiteFundo,
%%         +PontosVisitados)

%% Os vertices sao casos especiais, em que contam como se estivessem a
%% tocar em 2 lados
%% Primeiro caso: O vertice do topo do tabuleiro
caminho(X1, Y1, _, _, _, _, 1, _) :-
        e_vertice_topo(X1, Y1).

%% Segundo caso: O vertice no canto inferior esquerdo
caminho(X1, Y1, _, _, _, 1, _, _) :-
        e_vertice_inferior_esquerdo(X1, Y1).

%% Terceiro caso: O vertice no canto inferior direito
caminho(X1, Y1, _, _, 1, _, _, _) :-
        e_vertice_inferior_direito(X1, Y1).

%% Pontos que so tocam num dos lados podem ser pontos que terminam um caminho
caminho(X1, Y1, _, _, _, 1, 1, _) :-
        limite_esquerda(X1, Y1).

caminho(X1, Y1, _, _, 1, _, 1, _) :-
        limite_direita(X1, Y1).

caminho(X1, Y1, _, _, 1, 1, _, _) :-
        limite_fundo(X1, Y1).

%% Verificar os casos especiais dos vertices
caminho(X1, Y1, Tabuleiro, ValorPedra, _, _, LimiteFundo, PontosVisitados) :-
        e_vertice_topo(X1, Y1),
        !,
        proxima_pedra_caminho(X1, Y1, Tabuleiro, ValorPedra, PontosVisitados, X2, Y2),
        caminho(X2, Y2, Tabuleiro, ValorPedra, 1, 1, LimiteFundo, [X2|[Y2|PontosVisitados]]).

caminho(X1, Y1, Tabuleiro, ValorPedra, _, LimiteDireita, _, PontosVisitados) :-
        e_vertice_inferior_esquerdo(X1, Y1),
        !,
        proxima_pedra_caminho(X1, Y1, Tabuleiro, ValorPedra, PontosVisitados, X2, Y2),
        caminho(X2, Y2, Tabuleiro, ValorPedra, 1, LimiteDireita, 1, [X2|[Y2|PontosVisitados]]).

caminho(X1, Y1, Tabuleiro, ValorPedra, LimiteEsquerda, _, _, PontosVisitados) :-
        e_vertice_inferior_direito(X1, Y1),
        !,
        proxima_pedra_caminho(X1, Y1, Tabuleiro, ValorPedra, PontosVisitados, X2, Y2),
        caminho(X2, Y2, Tabuleiro, ValorPedra, LimiteEsquerda, 1, 1, [X2|[Y2|PontosVisitados]]).


%% Casos para os pontos que so tocam num lado, mas que ainda nao terminam um caminho
caminho(X1, Y1, Tabuleiro, ValorPedra, _, LimiteDireita, LimiteFundo, PontosVisitados) :-
        limite_esquerda(X1, Y1),
        proxima_pedra_caminho(X1, Y1, Tabuleiro, ValorPedra, PontosVisitados, X2, Y2),
        caminho(X2, Y2, Tabuleiro, ValorPedra, 1, LimiteDireita, LimiteFundo, [X2|[Y2|PontosVisitados]]).

caminho(X1, Y1, Tabuleiro, ValorPedra, LimiteEsquerda, _, LimiteFundo, PontosVisitados) :-
        limite_direita(X1, Y1),
        !,
        proxima_pedra_caminho(X1, Y1, Tabuleiro, ValorPedra, PontosVisitados, X2, Y2),
        caminho(X2, Y2, Tabuleiro, ValorPedra, LimiteEsquerda, 1, LimiteFundo, [X2|[Y2|PontosVisitados]]).

caminho(X1, Y1, Tabuleiro, ValorPedra, LimiteEsquerda, LimiteDireita, _, PontosVisitados) :-
        limite_fundo(X1, Y1),
        !,
        proxima_pedra_caminho(X1, Y1, Tabuleiro, ValorPedra, PontosVisitados, X2, Y2),
        caminho(X2, Y2, Tabuleiro, ValorPedra, LimiteEsquerda, LimiteDireita, 1, [X2|[Y2|PontosVisitados]]).

% Caso para os pontos que estao no meio do tabuleiro
caminho(X1, Y1, Tabuleiro, ValorPedra, LimiteEsquerda, LimiteDireita, LimiteFundo, PontosVisitados) :-
        !,
        proxima_pedra_caminho(X1, Y1, Tabuleiro, ValorPedra, PontosVisitados, X2, Y2),
        caminho(X2, Y2, Tabuleiro, ValorPedra, LimiteEsquerda, LimiteDireita, LimiteFundo, [X2|[Y2|PontosVisitados]]).

%% Predicado que indica se podemos adicionar um ponto ao caminho
%% A forma de verificar isso e contando o numero de vezes que um ponto
%% aparece na lista de pontos visitados.
%% Se o ponto ainda nao existir na lista de pontos visitados, entao e
%% um ponto valido
%% Se o ponto so aparecer uma vez na lista de pontos visitados, tambem
%% podemos considerar-lo como um ponto valido, porque tem que se
%% permitir percorrer um caminho no sentido inverso, para regressar a
%% alguma interseccao que possa existir
%% ponto_em_lista(+X, +Y, +Lista)
ponto_em_lista(X,Y, Lista) :-
        contar_ponto_em_lista(X, Y, Lista, N),
        ponto_em_lista(N).

ponto_em_lista(0).
ponto_em_lista(1).

%% Predicado que diz o numero de vezes que um ponto
%% aparece numa lista
%% contar_ponto_em_lista(+X, +Y, +Lista, -N)
contar_ponto_em_lista(X, Y, Lista, N) :-
        contar_ponto_em_lista(X, Y, Lista, 0, N).
contar_ponto_em_lista(_, _, [], Acum, Acum) :- !.
contar_ponto_em_lista(X, Y, [X|[Y|XS]], Acum, N) :-
        !,
        Acum1 is Acum + 1,
        contar_ponto_em_lista(X, Y, XS, Acum1, N).
contar_ponto_em_lista(X, Y, [_|[_|XS]], Acum, N) :-
        !,
        contar_ponto_em_lista(X, Y, XS, Acum, N).

%% TODO: Mudar o nome de obter_casa definida em cima para obter_n_coluna_linha
%% Obtem o valor que esta numa dada casa do tabuleiro
%% obter_casa(+NumeroLinha, +NumeroCasa, +Tabuleiro, -Resultado)
obter_casa(NumeroLinha, NumeroCasa, Tabuleiro, Resultado) :-
        obter_linha(NumeroLinha, Tabuleiro, LinhaTabuleiro),
        obter_casa(NumeroCasa, LinhaTabuleiro, Resultado).

%lista onde tem as posicoes de todas as casas
lista_casas([[0,6],
             [1,5],[1,7],
             [2,4],[2,6],[2,8],
             [3,3],[3,5],[3,7],[3,9],
             [4,2],[4,4],[4,6],[4,8],[4,10],
             [5,1],[5,3],[5,5],[5,7],[5,9],[5,11],
             [6,0],[6,2],[6,4],[6,6],[6,8],[6,10],[6,12]]).


retira_ponto([],_,_,_).
retira_ponto([[X,Y]|Z],Z,X,Y).

lista_casas_validas(Tabuleiro,X):-
	lista_casas(H),
	lista_casas_validas(Tabuleiro,H,X).
lista_casas_validas(_,[],[]):-!.
lista_casas_validas(Tabuleiro,Lista,[[X,Y]|XS]):-
	retira_ponto(Lista,V,X,Y),
	obter_casa(X,Y,Tabuleiro,Res),
	Res=:=1,!,
	lista_casas_validas(Tabuleiro,V,XS).

lista_casas_validas(Tabuleiro,[_|Lista],XS):-
	lista_casas_validas(Tabuleiro,Lista,XS).


%numero de elementos da lista
conta_elementos([],0).
conta_elementos([_|X],F):-
	conta_elementos(X,F1),
	F is F1+1.

%retira os pontos X e Y de um determinado elemento da lista
tira_ponto_lista([],_,_,_):-!.
tira_ponto_lista(Lista,0,X,Y):-!,retira_ponto(Lista,_,X,Y).
tira_ponto_lista([_|L],Nelemento,X,Y):-
	Elemento is Nelemento-1,
	tira_ponto_lista(L,Elemento,X,Y).





/*
        JOGADA ALEATORIA DO COMPUTADOR

	basicamente o computador escolhe uma jogada aleatoria
*/


%faz uma jogada do computador com casa branca
%os writes servem para testes
jogada_computador_branco(Tabuleiro, NovoTabuleiro) :-
	joga_aleatorio(Tabuleiro,5,NovoTabuleiro).



%faz uma jogada do computador com casa preta
%os writes sao para testes
jogada_computador_preto(Tabuleiro, NovoTabuleiro) :-
	joga_aleatorio(Tabuleiro,6,NovoTabuleiro).


%escolhe uma jogada aleatoria
joga_aleatorio(Tabuleiro,Peca,NovoTabuleiro):-
	lista_casas_validas(Tabuleiro,ListaJV),
	conta_elementos(ListaJV,Nel),
	random(0,Nel,Rnd),
	tira_ponto_lista(ListaJV,Rnd,X,Y),
	colocar_pedra(X,Y,Tabuleiro,Peca,NovoTabuleiro).
/*
        JOGADA INTELIGENTE DO COMPUTADOR

        COMO FUNCIONA:
        o jogador pesquisa todas as jogadas validas no tabuleiro e identifica a melhor jogada,
        seguindo varias regras listado em aval_jogada_* em que tem uma determinada  pontuacao.
        De todas as jogadas possiveis, aquela que tiver a maior pontuacao sera aquela em que vai ser jogada

        * = branca ou preta (foi necessario para identificar a peca em que se vai jogar)

*/



%faz uma jogada do computador com casa preta
%os writes sao para testes
jogada_computador_preto_intel(Tabuleiro, NovoTabuleiro) :-
        lista_casas_validas(Tabuleiro,H),
 melhor_jogada(H,Tabuleiro,0,0,0,NovoTabuleiro,6).



%faz uma jogada do computador com casa preta
%os writes sao para testes
jogada_computador_branco_intel(Tabuleiro, NovoTabuleiro) :-
        lista_casas_validas(Tabuleiro,H),
        melhor_jogada(H,Tabuleiro,0,0,0,NovoTabuleiro,5).

melhor_jogada([],Tabuleiro,W,_,_,NovoTabuleiro,Peca):-
	W=:=30,!,
	joga_aleatorio(Tabuleiro,Peca, NovoTabuleiro).

melhor_jogada([],Tabuleiro,W,NumLinha,NumCasa,NovoTabuleiro,Peca):-
	W>0,!,
	colocar_pedra(NumLinha,NumCasa, Tabuleiro, Peca, NovoTabuleiro).

melhor_jogada(Z,Tabuleiro,W,_,_,NovoTabuleiro,Peca):-
        retira_ponto(Z,L,X,Y),obter_casa(X,Y,Tabuleiro,Res),
        Res==1,
        aval_jogada(X,Y ,Tabuleiro,Pontos,Peca),
	W < Pontos,!, M is Pontos,NumLinha1 is X, NumCasa1 is Y,
        melhor_jogada(L,Tabuleiro,M,NumLinha1,NumCasa1,NovoTabuleiro,Peca).

melhor_jogada(Z,Tabuleiro,W,NumLinha,NumCasa,NovoTabuleiro,Peca):-
        retira_ponto(Z,L,_,_),
        melhor_jogada(L,Tabuleiro,W,NumLinha,NumCasa,NovoTabuleiro,Peca).


%avaliacoes para a jogada do computador
aval_jogada(NumeroLinha,NumeroCasa ,Tabuleiro,W,6):-
	num_vizinhos(NumeroLinha,NumeroCasa ,Tabuleiro,5,Y), Y>1,!,W is Y*17.
aval_jogada(NumeroLinha,NumeroCasa ,Tabuleiro,W,5):-
	num_vizinhos(NumeroLinha,NumeroCasa ,Tabuleiro,6,Y), Y>1,!,W is Y*17.
aval_jogada(NumeroLinha,NumeroCasa ,_,20,_):- limite_esquerda(NumeroLinha,NumeroCasa),!.
aval_jogada(NumeroLinha,NumeroCasa ,_,20,_):- limite_fundo(NumeroLinha,NumeroCasa),!.
aval_jogada(NumeroLinha,NumeroCasa ,_,20,_):- limite_direita(NumeroLinha,NumeroCasa),!.
aval_jogada(_,_ ,_,30,_).




%Determina o numero de vizinhos
num_vizinhos(NumeroLinha,NumeroCasa ,Tabuleiro,NumeroPeca,V):-
findall(ligacao(X, Y), existe_ligacao(NumeroLinha,NumeroCasa, X, Y), G),
lista_casas_pecas_vizinhas(G,Tabuleiro,L), n_vizinhos(L,NumeroPeca,V).

%Cria uma lista com as pecas vizinhas
lista_casas_pecas_vizinhas([],_,[]).
lista_casas_pecas_vizinhas([Z|G],Tabuleiro,[Peca|LPeca]):-
                                         x_y_ligacao(Z, X, Y),
                                         obter_casa(X, Y, Tabuleiro, Peca),
                                         lista_casas_pecas_vizinhas(G,Tabuleiro,LPeca).

%predicado auxiliar de num_vizinhos
n_vizinhos(Lista, NumeroPeca,N) :-
                  n_vizinhos(Lista, NumeroPeca,N, 0).
n_vizinhos([],_,Acum, Acum).
n_vizinhos([Z|G],NumeroPeca,N, Acum):-
	Z=:=NumeroPeca,!, Acum1 is Acum+1,
	n_vizinhos(G,NumeroPeca,N,Acum1).
n_vizinhos([_|G],NumeroPeca,N, Acum):-n_vizinhos(G,NumeroPeca,N,Acum).


x_y_ligacao(ligacao(X,Y), X, Y).


%% Predicado que inicia um jogo entre o computador e si mesmo
luta_entre_computadores :-
        estado_inicial(Tabuleiro),
        luta_entre_computadores(Tabuleiro).

luta_entre_computadores(TabuleiroActual) :-
        jogada_computador_branco(TabuleiroActual, TabuleiroBranco),
        nl,
        mostrar_tabuleiro(TabuleiroBranco),
        !,
        \+jogo_terminado(TabuleiroBranco),
        get_single_char(_),
        jogada_computador_preto(TabuleiroBranco, TabuleiroPreto),
        nl,
        mostrar_tabuleiro(TabuleiroPreto),
        !,
        \+jogo_terminado(TabuleiroPreto),
        get_single_char(_),
        !,
        luta_entre_computadores(TabuleiroPreto).

%%Predicado em que os computadores nao usam apenas jogadas aleatorias
luta_entre_computadores_intel :-
        estado_inicial(Tabuleiro),
        luta_entre_computadores_intel(Tabuleiro).

luta_entre_computadores_intel(TabuleiroActual) :-
        jogada_computador_branco_intel(TabuleiroActual, TabuleiroBranco),
        nl,
        mostrar_tabuleiro(TabuleiroBranco),
        !,
        \+jogo_terminado(TabuleiroBranco),
        get_single_char(_),
        jogada_computador_preto_intel(TabuleiroBranco, TabuleiroPreto),
        nl,
        mostrar_tabuleiro(TabuleiroPreto),
        !,
        \+jogo_terminado(TabuleiroPreto),
        get_single_char(_),
        !,
        luta_entre_computadores_intel(TabuleiroPreto).



%% Codigo do joao
%% Menu principal

i :- inicio.

inicio :-
        repeat,
        write('\n'),
        write('*** Modo de jogo ***\n'),
        write(' 1- Jogador vs Pc\n'),
        write(' 2- Jogador vs Jogador\n'),
        write(' 3- Pc vs Pc (Nivel fraco)\n'),
	write(' 4- Pc vs Pc (Nivel forte)\n'),
        write(' 0- Sair\n'),
        write('\nOpcao: '),
        read(C),
        estado_inicial(Tabuleiro),
        opcao(C , Tabuleiro).

%% "Switch" das opcoes do menu
%% opcao(+Numero, +Tabuleiro)
opcao(0, _) :- !.
opcao(1, Tabuleiro) :- !, jogador_vs_pc(Tabuleiro).
opcao(2, Tabuleiro) :- !, jogador_vs_jogador(Tabuleiro).
opcao(3, Tabuleiro) :- !, pc_vs_pc(Tabuleiro).
opcao(4, Tabuleiro) :- !, pc_vs_pc_intel(Tabuleiro).

%% Menu para a escolha das pecas para o jogador
%% jogador_vs_pc(+Tabuleiro).
jogador_vs_pc(Tabuleiro):-
        repeat,
        write('\n'),
        write('*** Modo de jogo ***\n'),
        write(' 1- Jogar com as Pecas Brancas\n'),
        write(' 2- Jogar com as Pecas Pretas\n'),
        write('\nOpcao: '),
        read(C),
        jogador_vs_pc_selecionar_intel(Tabuleiro, C).

%% Predicado que permite selecionar o nivel da inteligencia que
%% o computador devera de ter
%% jogador_vs_pc_selecionar_intel(+Tabuleiro, +CorPecasJogador)
jogador_vs_pc_selecionar_intel(Tabuleiro, 1) :-
	!,
	repeat,
	nl,
	writeln('Inteligencia do computador'),
	writeln(' 1- Fraca'),
	writeln(' 2- Boa'),
	read(C),
	\+jogador_vs_pc(Tabuleiro, 1, C), % Quando houver um vencedor,
	                                  % este predicado falha
	!, % regressar ao menu
	inicio.

jogador_vs_pc_selecionar_intel(Tabuleiro, 2) :-
	!,
	repeat,
	nl,
	writeln('Inteligencia do computador'),
	writeln(' 1- Fraca'),
	writeln(' 2- Boa'),
	read(C),
	\+jogador_vs_pc(Tabuleiro, 2, C), % Quando houver um vencedor,
					  % este predicado falha
	!, % regressar ao menu
	inicio.

%% Predicado que le do utilizador uma jogada
%% ler_jogada_utilizador(-Numero, -Letra)
ler_jogada_utilizador(N, L) :-
        write('\nLetra da Casa: '), read(L),
        write('Numero da Casa: '), read(N).

%% Predicado que le do utilizador uma jogada valida (pode ser passada
%% para uns dos predicados colocar_pedra)
%% ler_jogada_utilizador_valida(-Numero, -Letra, +Tabuleiro)
ler_jogada_utilizador_valida(N, L, Tabuleiro) :-
        repeat,
        ler_jogada_utilizador(N, L),
        validar_jogada(N, L, Tabuleiro).

%% Predicado que indica que o conjunto (Numero, Letra) corresponde a
%% uma jogada valida. Caso seja valida, termina com sucesso, senao
%% falha com uma mensagem de erro.
%% validar_jogada(+Numero, +Letra, +Tabuleiro)
validar_jogada(N, L, Tabuleiro) :-
        jogada_valida(N, L, Tabuleiro).
validar_jogada(_, _, _) :-
        writeln('Jogada nao valida'),
        fail.

%% Predicado que permite o jogador jogar contra o pc. Se Status for 1,
%% o jogador joga com as pecas brancas e o computador com as pecas
%% pretas, se for 2, o jogador joga com as pecas pretas e o computador
%% com as pecas brancas.
%% Se a Inteligencia for 1, e usado o algoritmo aleatorio, se for 2
%% usa-se o algoritmo minimax.
%% jogador_vs_pc(+Tabuleiro, +Status, +Inteligencia)
jogador_vs_pc(Tabuleiro, 1, 1):-
        mostrar_interface(Tabuleiro, 0),
        ler_jogada_utilizador_valida(N, L, Tabuleiro),
        colocar_pedra_branca(N, L, Tabuleiro, Novo_Tabuleiro),
        !, % Fixar a jogada
        \+jogo_terminado(Novo_Tabuleiro),
        jogada_computador_preto(Novo_Tabuleiro, NovoTabuleiro2),
        !,
        \+jogo_terminado(NovoTabuleiro2),
        !,
        jogador_vs_pc(NovoTabuleiro2, 1, 1).

jogador_vs_pc(Tabuleiro, 2, 1):-
        jogada_computador_branco(Tabuleiro, Novo_Tabuleiro),
        !,
        \+jogo_terminado(Novo_Tabuleiro),
        mostrar_interface(Novo_Tabuleiro, 1),
        ler_jogada_utilizador_valida(N, L, Novo_Tabuleiro),
        colocar_pedra_preta(N, L, Novo_Tabuleiro, NovoTabuleiro2),
        !, % Fixar a jogada
        \+jogo_terminado(NovoTabuleiro2),
        !,
        jogador_vs_pc(NovoTabuleiro2, 2, 1).

jogador_vs_pc(Tabuleiro, 1, 2) :-
	mostrar_interface(Tabuleiro, 0),
	ler_jogada_utilizador_valida(N, L, Tabuleiro),
	colocar_pedra_branca(N, L, Tabuleiro, Novo_Tabuleiro),
	!, % Fixar a jogada
	\+jogo_terminado(Novo_Tabuleiro),
	jogada_computador_preto_intel(Novo_Tabuleiro, NovoTabuleiro2),
	!,
	\+jogo_terminado(NovoTabuleiro2),
	!,
	jogador_vs_pc(NovoTabuleiro2, 1, 2).

jogador_vs_pc(Tabuleiro, 2, 1):-
        jogada_computador_branco_intel(Tabuleiro, Novo_Tabuleiro),
        !,
        \+jogo_terminado(Novo_Tabuleiro),
        mostrar_interface(Novo_Tabuleiro, 1),
        ler_jogada_utilizador_valida(N, L, Novo_Tabuleiro),
        colocar_pedra_preta(N, L, Novo_Tabuleiro, NovoTabuleiro2),
        !, % Fixar a jogada
        \+jogo_terminado(NovoTabuleiro2),
        !,
        jogador_vs_pc(NovoTabuleiro2, 2, 1).


%% Predicado que permite o jogador jogar contra outro jogador
%% jogador_vs_jogador(+Tabuleiro)
jogador_vs_jogador(Tabuleiro):-
        %% As pecas brancas jogam primeiro
        jogador_vs_jogador(Tabuleiro, 0).

%% Predicados que processam a jogada de um jogador. Se o Status for 0,
%% corresponde a jogada dos jogador das pecas brancas, se for 1
%% corresponde a jogada do jogador das pecas pretas
%% jogador_vs_jogador(+Tabuleiro, +Status)
jogador_vs_jogador(Tabuleiro, 0):-
        mostrar_interface(Tabuleiro, 0),
        ler_jogada_utilizador_valida(N, L, Tabuleiro),
        colocar_pedra_branca(N, L, Tabuleiro, Novo_Tabuleiro),
        !,
        \+jogo_terminado(Novo_Tabuleiro),
        !,
        %% Passar para a jogada das pecas pretas
        jogador_vs_jogador(Novo_Tabuleiro, 1).

jogador_vs_jogador(Tabuleiro, 1):-
        mostrar_interface(Tabuleiro, 1),
        ler_jogada_utilizador_valida(N, L, Tabuleiro),
        colocar_pedra_preta(N, L, Tabuleiro, Novo_Tabuleiro),
        !,
        \+jogo_terminado(Novo_Tabuleiro),
        !,
        %% Passar para a jogada das pecas brancas
        jogador_vs_jogador(Novo_Tabuleiro, 0).

%% Predicado para o jogo entre o computador e si mesmo
%% Jogo com algoritmo aleatorio
pc_vs_pc(Tabuleiro):-  luta_entre_computadores(Tabuleiro).
%% Jogo com algoritmo minimax
pc_vs_pc_intel(Tabuleiro) :- luta_entre_computadores_intel(Tabuleiro).

%teste :- estado_final3(X), jogo_terminado(X).
